package fctuc.dei.myTwitter.client;

import static fctuc.dei.myTwitter.constants.MyTwitterConstants.BASE_HOST;
import static fctuc.dei.myTwitter.constants.MyTwitterConstants.NIO_LISTENER_PORT;
import static fctuc.dei.myTwitter.constants.MyTwitterConstants.TCP_LISTENER_PORT;

import java.nio.channels.SelectionKey;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;

import fctuc.dei.myTwitter.com.Connection;
import fctuc.dei.myTwitter.com.Envelope;
import fctuc.dei.myTwitter.com.MT;
import fctuc.dei.myTwitter.com.Message;
import fctuc.dei.myTwitter.com.MessageHandler;
import fctuc.dei.myTwitter.pojo.Tweet;
import fctuc.dei.myTwitter.pojo.User;




public class MyTwitterClient implements MessageHandler, Runnable{

	CommandLine com;
	boolean firstConnection = true;
	public User currentUser;
	public ServerConnection serverConnection = null;

	public String host = "";
	public int listenerPort;
	public Semaphore sem;
	public Semaphore initialConnectionSemaphore;
	public Semaphore runModeSemaphore;
	public boolean benchmarkMode;
//	public BlockingQueue<Envelope> inbox;
	public String tech;
	public MT displayMode;
	private List<TwitterClientListener> listeners;
	
	public void run(){
		this.runServer();
	}
	
	public MyTwitterClient(String serverType, boolean mode){
		
		this.tech = serverType;
		this.benchmarkMode = mode;
		this.listeners = new ArrayList<TwitterClientListener>();
	}
	

	public void runServer(){
		this.host = BASE_HOST;
		this.sem = new Semaphore(1, true);
		this.initialConnectionSemaphore = new Semaphore(0, true);
		this.runModeSemaphore = new Semaphore(0, true);
		this.displayMode = MT.ASYNC_DISPLAY;
//		this.inbox = new LinkedBlockingQueue<Envelope>();
		System.out.println(tech);
		if(this.tech.equalsIgnoreCase("tcp")){
			this.listenerPort = TCP_LISTENER_PORT;
			this.serverConnection = new SocketConnection(this);
		}else
		if(this.tech.equalsIgnoreCase("nio")){
			this.listenerPort = NIO_LISTENER_PORT;
			this.serverConnection = new NewNioConnection(this);
		}else
		if(this.tech.equalsIgnoreCase("rmi")){
			this.serverConnection = new RMIConnection(this);
		}
		else{
			System.out.println("bah... isto assim nao vai la");
			System.exit(-1);
		}
		
		serverConnection.connect();
		
		com = new CommandLine(serverConnection, sem);
		
		if(!this.benchmarkMode){
			this.initiateConnection();
			
			/*
			 * Aqui espera pela primeira resposta
			 */
			try{
				this.initialConnectionSemaphore.acquire();
			}catch (InterruptedException e){
				e.printStackTrace();
			}
			this.initialConnectionSemaphore.release();
			
			/*
			 * Mostra o menu inicial pela primeira vez
			 */
			
			com.start();
			try {
				com.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void addListener(TwitterClientListener l){
//		System.out.println(l);
		this.listeners.add(l);
	}
	
	public void removeListener(TwitterClientListener l){
		this.listeners.remove(l);
	}
	
	public void initiateConnection() {
		
		if(firstConnection){
			firstConnection = false;
			switch(com.showInit()){
			case REGISTER:
				currentUser = com.showRegister();
				serverConnection.send(MT.REGISTER,	"USER", currentUser);
				break;
			case LOGOUT:
				this.endSession();
				break;
			case LOGIN:
				currentUser = com.showLogin();
				serverConnection.send(MT.LOGIN, "USER", currentUser, "RECONNECT", "FALSE");
				break;
			}
		}else{
			serverConnection.send(MT.LOGIN, "USER", currentUser, "RECONNECT", "TRUE");
		}
	}

	public void endSession(){
		serverConnection.terminate();
		for (TwitterClientListener listener : this.listeners) {
			listener.sessionTerminated();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public synchronized boolean handleMessage(Message m, Connection c) {
		switch(m.type){
		case REGISTER_RESULT:
			if(m.get("RESULT").equals("OK")){
				this.currentUser = (User) m.get("USER");
				this.initialConnectionSemaphore.release();
			}
			else{
				com.write("\nTry again with another username and email");
				this.currentUser = new User();
				this.initiateConnection();
			}
			break;
		case LOGIN_RESULT:
			if(m.get("RESULT").equals("OK")){
				this.currentUser = (User) m.get("USER");
				this.initialConnectionSemaphore.release();
			}
			else{
				com.write("\nWrong password. Try again");
				this.currentUser = new User();
				this.firstConnection = true; //trata como se fosse o primeiro login
				this.initiateConnection();
			}
			break;
		case LIST_TWEETS:
			ArrayList<Tweet> tweetList = (ArrayList<Tweet>) m.get("TWEET_LIST");
			//show tweets
			for(Iterator<Tweet> it = tweetList.iterator();it.hasNext();){
				Tweet t = it.next();
				com.write(">>> by "+t.getAuthor()+" at : "+t.getTimeStamp());
				com.write("    ["+t.getMessage()+"]\n");
			}
//			this.answerReceived();
			break;
		case FOLLOW:
			if(m.getStr("RESULT").equals("OK"))
				com.write("You are now following " + m.getStr("USER"));
			else
				com.write(m.getStr("CAUSE"));
//			this.answerReceived();
			break;
		case FOLLOWERS:
			ArrayList<User> followers = (ArrayList<User>) m.get("FOLLOWERS");
			if(followers.isEmpty())
				com.write("Sorry, no followers yet\n");
			else{
				com.write("You are being followed by:\n");
				for(Iterator<User> it = followers.iterator(); it.hasNext();){
					User usr = it.next();
					com.write(" > "+usr.getUsername()+" - " + usr.getBio() + " - " + usr.getLocation());
				}
			}
//			this.answerReceived();
			break;
		case FOLLOWING:
			ArrayList<User> following = (ArrayList<User>) m.get("FOLLOWING");
			if(following.isEmpty())
				com.write("You need to get some friends\n");
			else{
				com.write("People that you follow:\n");
				for(Iterator<User> it = following.iterator(); it.hasNext();){
					User usr = it.next();
					com.write(" > "+usr.getUsername()+" - " + usr.getBio() + " - " + usr.getLocation());
				}
			}
//			this.answerReceived();
			break;
		case SEARCH:
			ArrayList<User> searchResults = (ArrayList<User>) m.get("RESULTS");
			if(searchResults.isEmpty())
				com.write("Sorry, no matches for your query\n");
			else{
				com.write("Users that match you query:\n");
				for(Iterator<User> it = searchResults.iterator(); it.hasNext();){
					User usr = it.next();
					com.write(" > "+usr.getUsername()+" - " + usr.getBio() + " - " + usr.getLocation() +"\n");
				}
			}
//			this.answerReceived();
			break;
		case TWEET:
			if(this.displayMode == MT.ASYNC_DISPLAY)
				com.write("You have some new tweets...\n");
			else{
				Tweet t = (Tweet) m.get("TWEET");
				com.write(">>> by "+t.getAuthor()+"     ["+t.getMessage()+"]\n");
			}
			break;
		case LOGOUT:
			this.endSession();
			break;
		default:
			com.write("<<< RESPOSTA SERVIDOR: "+m.type.toString() +"\n");
			break;
		}
		return true;
	}

	@Override
	public void handleNioMessage(Message m, SelectionKey sk) {
		// TODO Auto-generated method stub
		
	}
}

